/** \defgroup qgetopt
  \brief A Qt library to manage command line arguments.
  It will accept long options that start with a double hyphen and
  optionally short options that will start with a single hyphen.

  Any option can have arguments passed to it.
  The arguments can be optional signified by being in square brackets.
  The program itself can also have options, these however would be compulsory.

  To use the library there are two stages,
  first define the options that are permitted
  then parse the options that have been passed in.

  There is always a default --help and -h switch that can be used to display the available switches.

  The following snippet illustrates how to define the permitted options.
  \code
   OptManager * om = OptManager::getOptMgr(argv[0]);
   om->addAppArgs("FILE");
   om->addSwitch("verbose",verbose, "v", "[LEVEL]");
  \endcode
  An instance of the OptManager class is returned by the static method OptManager::getOptMgr(),
  which for the first time it is called expects to receive a QString with the name of the program.

  Program arguments can be added by the method OptManager::addAppArgs()

  A switch can be added by the method OptManager::addSwitch(), in the above instance the switch
  is called verbose with a short option of v it has an optional argument LEVEL and if
  it is used in teh program then it will call a function called verbose,
  which may be defined as follows:
  \code
   int verboseLevel=0;

   void verbose(const OptSwitch * opt)
   {
      QString arg = opt->argList();
      int level;
      bool ok = true;
      if (arg.isEmpty())
         level = 1;
      else
         level = arg.toInt(&ok,10);
      if (!ok)
         level = 1;
      verboseLevel = level;
   }
  \endcode

  The following snippet illustrates how to define process the options passed in.
  \code
   OptManager * om = OptManager::getOptMgr();
   om->parse(argc, argv);
  \endcode

 * \attention This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * \attention This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * \attention You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the
 * Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307  USA
 *
   \author    Graeme
   \date Copyright (C) 2008-9  Graeme Foster
 */
#ifndef OPT_MANAGER_H
#define OPT_MANAGER_H

#include <QtCore>

#include "optArgument.h"
class OptSwitch;

/*! pointer to method that is used to initialise switch data */
typedef void (* InitFuncPtr)(const QStringList&);
/*! pointer to method that is used to perform the action */
typedef void (* ActionFuncPtr)(const OptSwitch*);
/*! pointer to method that is used after the action has been performed */
typedef void (* CloseFuncPtr)();
#include "optSwitch.h"

/*! \ingroup qgetopt
    \brief The OptManager class is a manager class for the options.
           It has two main roles to record the possible options the program has
           and then to determine which options the users has selected.

   <h3>Overview</h3>
   This is a singleton class, the constructor is private and the instance of the
   class is obtained via the static method getOptMgr().

   A program may have two types of options; \em switches and \em arguments.
   A \em switch can be described as a request for an action to happen and is managed by the OptSwitch class,
   whilst an \em argument is data that is passed into the program and is managed by the OptArgument class.
   A switch may also have arguments.

   A switch is preceded by a single minus sign and a single character (short switch),
   or a double minus sign and then the descriptive name of the switch (long switch).
   Short switches can be combined together. For example:
   <table>
   <tr><td>-h</td><td>short switch</td></tr>
   <tr><td>--help</td><td>long switch</td></tr>
   <tr><td>-lvd</td><td>three short switches</td></tr>
   </table>

   A switch argument will appear after the switch with a space separating them.
   When a argument follows multiple short switches the argument is associated with the last switch.
   Program arguments appear at the end of the list of options and are each separated by a space. For example:
   <table>
   <tr><td>--log myprog.log</td><td>the argument <em>myprog.log</em> is associated with the switch <em>--log</em></td></tr>
   <tr><td>-vl myprog.log</td><td>the argument <em>myprog.log</em> is associated with the switch <em>-l</em> and not -v</td></tr>
   </table>

   For a program all arguments are mandatory, whilst for a switch arguments can be optional.
   <em>The reason that program arguments are mandatory is that it may not be possible to distinguish
   between an optional program argument and an optional switch argument.</em>
   If it transpires that it is necessary for the program to have optional arguments this can be done, for example,
   by adding a switch called --optionalArguments and listing them there.
   This obviously will require the user to implement this switch.

   <h3>Usage</h3>
   ...Or how to use this class.

   The following example establishes a switch --verbose or just -v which has an optional LEVEL argument.
   The program expects single argument to be passed in.
   By default there will always be a --help or -h option.
   If the verbose option is set from the command line then the function verbose() will be automatically called.

   \code
#include "optManager.h"
#include <QtCore>

void setOptions(QString ProgName);
int verboseLevel=0;

void verbose(const OptSwitch * opt)
{
   QString arg = opt->argList();
   int level;
   bool ok = true;
   if (arg.isEmpty())
      level = 1;
   else
      level = arg.toInt(&ok,10);
   if (!ok)
      level = 1;
   verboseLevel = level;
}

int main(int argc, char *argv[])
{
   setOptions(argv[0]);
   OptManager * om = OptManager::getOptMgr();
   om->parse(argc, argv);

   QTextStream console(stdout);
   console << "Verbose level set to \"" << verboseLevel <<"\"\n";

   om->close();
   return 0;
}

void setOptions(QString ProgName)
{
   OptManager * om = OptManager::getOptMgr(ProgName);
   om->addAppArgs("FILE");
   om->addSwitch("verbose",verbose, "v", "[LEVEL]");
}
\endcode
   <h3>Internals</h3>
   ...Or how does this class work?

   As already discussed, there are two phases namely, to record the possible options the application has
   and then to determine which options the users has selected. Each of these will now be discussed in detail:

   <h4>Record an applications potential options</h4>
   When the OptManager instance is created the appName is passed in and the appName and path details are extracted by the
   constructor.
   General details about the application can be added by calling the addDescription() method.
   A switch can be added by using the addSwitch() method. This method returns a pointer to the OptSwitch class
   and so this pointer can be used to add a description to the switch by using the OptSwitch::addDescription() method.
   Any application specific methods that are required to be run before any switches should be wrapped in a method
   and set up by calling addInitMethod().
   <h4>Determine the options a user has selected</h4>
   Typically there are two methods that need to be added to the applicaiton parse() and then close()
   any application specific code that needs to be run should appear between these two method calls.
   <h5>The anatomny of parse()</h5>
   The parse() method is key to the functionality of this library. The method steps through each argument
   that has been passed into the application, and determines if it is a switch or an argument to either a switch or the application,
   (remember all switches start with a special character to distinguish them from an argument).
   Once this method has stepped through all the arguments that have been passed in it will have two lists:
   * A list of switches that have been selected (activeSwitches)
   * A list of arguments to the application (actualAppArgList)
   Then the execute method will be called, it will loop through each of the entries in the activeSwitches and if it finds
   any with the finishProcessing flag set to true it will run this method and the exit the application.
   Next it will run any initMethod passing it the list of arguments to the application.
   Finally it will loop through each of the entries in the activeSwitches and run their code.
*/
 /*! \todo Add a automatic switch --clopts FILE that will read a file of command line options
     \todo Change parseArgument() so that if the last switch is in error it will remove it from activeSwitches
     \todo Add an execute function for the app to be executed before any non conclude methods
 */
class OptManager
{
   private:
      //! \internal Pointer to the instance of this class
      static OptManager * _instancePtr;

   public:
      //! Method to return the instance of this class
      static OptManager * getOptMgr(QString appFullName="");

      //! Method to delete the instance
      static void clear(){delete _instancePtr;_instancePtr=0;}

      //! Method to display the help screen
      /*!
          This will display brief details on how to use the program. The details
          are obtained from the descriptions that have been added along with the
          switches that have been defined.
       */
      static void help(const OptSwitch*)
      {
         QTextStream console(stdout);
         console << getOptMgr()->pHelp();
         console.flush();
      }

   private:
      //! \internal The name of the application
      QString _appName;
      //! \internal The path to the application
      QString _path;
      //! \internal The list of command line arguments passed in
      QString _cmdLine;
      //! \internal A description of the application
      QString _description;
      //! \internal The initialisation method
      InitFuncPtr _initMethodPtr;
      //! \internal The list of switches that the application will accept
      QList <OptSwitch *> _switchList;
      //! \internal The list of switches that the application has received
      QList<OptSwitch *> _activeSwitches;
      //! \internal The list of arguments that the application will expect
      QList <OptArgument *> _argList;
      //! \internal The application arguments that have actually been passed in
      QStringList _actualAppArgList;
      //! \internal A list of warning messages that have been raised
      QStringList _warning;
      //! \internal A list of error messages that have been raised
      QStringList _error;
   private:
      //! \internal Private constructor for singleton class
      OptManager(QFileInfo appFullName);
      //! \internal Private copy constructor for singleton class
      /*! \internal This should not be called so an exception is thrown */
      OptManager(const OptManager&){throw "OptManager copy constructor called";}
      //! \internal return true if \em param is a recognised switch
      bool isSwitch(QString param);
      //! \internal return true if \em param is a recognised short switch
      bool isShortSwitch(QString param);
      //! \internal return true if \em param is a recognised long switch
      bool isLongSwitch(QString param);
      //! \internal return true if \em param is a number
      bool isNumber(QString param);
      //! \internal return true if the short switch is unique
      bool uniqueAbbrev(QChar abbrev);
      //! \internal return an instance of OptSwitch from \em opt which has been passed in
      OptSwitch * parseSwitch(QString opt);
      //! \internal return an instance of OptSwitch from \em opt which has been passed in
      OptSwitch * parseShortSwitchList(QString opt);
      //! \internal return an instance of OptSwitch from \em opt which has been passed in
      OptSwitch * parseShortSwitch(QChar opt);
      //! \internal return an instance of OptSwitch from \em opt which has been passed in
      OptSwitch * parseLongSwitch(QString opt);
      //! \internal Sets up the arguments for the program or for a switch
      bool parseArgument(OptSwitch * sw, QStringList &argQueue, bool more);
      //! \internal Sets up the arguments for a switch
      bool parseSwitchArgument(OptSwitch * sw, QStringList &argQueue);
      //! \internal Method to ensure that the appropriate execute methods are called for each switch
      void execute();

      //! \internal Display the help screen.
      QString pHelp();
   public:
      //! Method to return the name of the application
      QString getAppName(){return _appName;}
      //! Method to return the description associated with the application
      QString getAppDesc(){return _description;}
      //! Method to return the value for a specific argument passed into the application
      /*! This method will return the <em>i</em><sup>th</sup> argument.
          If there is no <em>i</em><sup>th</sup> argument then blank is returned. */
      QString getAppArg(int i){return ((i>=0) && (i<_actualAppArgList.size()))? _actualAppArgList[i]:"";}
      //! Method will return the arguments provided for a given switch
      QStringList getSwitchDetails(QString opt);
      //! Method to record the description of the program
      /*! This method will add \em detail as a description of the program.
          This description is used by the automatic --help switch. */
      void addDescription(QString detail){_description=detail;}
      //! Method to add details about the arguments for the application
      void addAppArgs (QString args);
      //! Method to return details about a selected application argument
      QString getAppArgTemplate (int i){return ((i>=0) && (i<_argList.size()))? _argList[i]->help():"";}
      //! Method to return details for a selected switch
      QString switchDumpTemplate(int i);
      //! Method to add a method that should be run before any other methods
      /*! This method set up a initialisation method for the program which will receive
          the program arguments. This means that an object can be set up before the methods
          for the other switchesis run.

          Any switch that has the conclude flag set to true will be run first and so
          in this specific case the initMethod will not be run. */
      void addInitMethod(InitFuncPtr initMethod){_initMethodPtr=initMethod;}
      //! Method to add a switch to the program
      OptSwitch * addSwitch(QString longName
                            ,ActionFuncPtr actionMethod
                            ,QChar abbrev=QChar::Null
                            ,QString args=""
                            ,CloseFuncPtr exitMethod=0
                            ,switchStyle style=continueRunning
                            );

      //! Method to parse the command line arguments
      QString parse(int argc, char *argv[], bool quiet=false);
      //! Method should always be called when the program finishes
      void close();
      //! Method to return the errors
      QString getErrors();
      //! Method to return the warnings
      QString getWarnings();
      //! Method to return the switches that have been selected
      QString switchDump();
      //! Method to return the program arguments that have been provided
      QString appArgDump();
      //! Method to return both the switches and program arguments
      QString dump(){return _appName + appArgDump() + "\n" + switchDump() + "\n";}
};

#endif
